2. Manipulación del DOM y Eventos

La manipulación del DOM y el manejo de eventos son pilares clave en el desarrollo de interfaces dinámicas con JavaScript. El DOM (Document Object Model) es la estructura jerárquica de un documento HTML que JavaScript puede modificar en tiempo real. Los eventos permiten a los desarrolladores capturar acciones del usuario, como clics, teclas presionadas, desplazamientos, etc., para modificar la página de forma interactiva.

1. Manipulación del DOM

La manipulación del DOM permite modificar elementos, contenido y estilos de una página web en respuesta a eventos o lógicas definidas. Esto permite generar experiencias dinámicas e interactivas para los usuarios.

1.1 Selección de elementos en el DOM

Para trabajar con el DOM, lo primero es seleccionar los elementos con los que se va a interactuar. JavaScript ofrece varios métodos para acceder a los elementos del DOM:

// Ejemplo: seleccionar un elemento por su ID
let titulo = document.getElementById('titulo');

1.2 Modificación de elementos

Una vez seleccionado un elemento, podemos modificar su contenido, estilo o atributos:

// Cambiar el contenido del título
titulo.textContent = 'Nuevo Título';
// Cambiar el color del texto
titulo.style.color = 'blue';
// Cambiar el valor de un atributo
titulo.setAttribute('class', 'nuevo-estilo');

1.3 Creación y eliminación de elementos

JavaScript permite agregar nuevos elementos al DOM o eliminar los existentes:

let nuevoParrafo = document.createElement('p');
nuevoParrafo.textContent = 'Este es un nuevo párrafo';
document.body.appendChild(nuevoParrafo);
let parrafo = document.querySelector('p');
parrafo.remove();

2. Eventos

Los eventos en JavaScript permiten a las aplicaciones web responder a interacciones del usuario. Un evento puede ser cualquier cosa, desde un clic, hasta el movimiento del ratón, la escritura en un campo de texto o la carga de la página.

2.1 Tipos de eventos comunes

2.2 Escuchar eventos

Para ejecutar código cuando ocurre un evento, se deben asignar "listeners" (escuchadores de eventos) a los elementos. La forma más común es usar addEventListener().

// Escuchar el evento 'click' en un botón
let boton = document.getElementById('miBoton');
boton.addEventListener('click', function() {
  alert('Botón clickeado!');
});

2.3 Prevención del comportamiento por defecto

Algunos eventos tienen comportamientos predeterminados que no siempre son deseados, como la recarga de la página al enviar un formulario. Con event.preventDefault(), es posible evitarlo:

let formulario = document.querySelector('form');
formulario.addEventListener('submit', function(event) {
  event.preventDefault(); // Evita que la página se recargue
  console.log('Formulario enviado');
});

2.4 Propagación de eventos

Los eventos pueden propagarse en el DOM a través de dos fases: la fase de captura y la fase de burbuja. Esto significa que un evento, como un clic, puede propagarse desde el elemento más específico (el que recibe el clic) hasta el más general (por ejemplo, document).

Se puede detener la propagación del evento utilizando event.stopPropagation().

let innerDiv = document.getElementById('inner');
let outerDiv = document.getElementById('outer');

innerDiv.addEventListener('click', function(event) {
  console.log('Clic en inner div');
  event.stopPropagation(); // Detiene la propagación
});

outerDiv.addEventListener('click', function() {
  console.log('Clic en outer div');
});

En este ejemplo, solo se registrará el clic en el innerDiv, y no en el outerDiv, gracias a stopPropagation().

2.4.1. Ejemplo básico de burbuja y captura

<div id="outer" style="padding: 50px; background-color: lightblue;">
  Outer Div
  <div id="middle" style="padding: 50px; background-color: lightgreen;">
    Middle Div
    <div id="inner" style="padding: 50px; background-color: lightcoral;">
      Inner Div
    </div>
  </div>
</div>

<script>
  let innerDiv = document.getElementById('inner');
  let middleDiv = document.getElementById('middle');
  let outerDiv = document.getElementById('outer');

  // Fase de burbuja (por defecto)
  innerDiv.addEventListener('click', function(event) {
    console.log('Clic en inner div (burbuja)');
  });

  middleDiv.addEventListener('click', function(event) {
    console.log('Clic en middle div (burbuja)');
  });

  outerDiv.addEventListener('click', function(event) {
    console.log('Clic en outer div (burbuja)');
  });

  // Fase de captura
  innerDiv.addEventListener('click', function(event) {
    console.log('Clic en inner div (captura)');
  }, true);

  middleDiv.addEventListener('click', function(event) {
    console.log('Clic en middle div (captura)');
  }, true);

  outerDiv.addEventListener('click', function(event) {
    console.log('Clic en outer div (captura)');
  }, true);
</script>

Explicación:

  1. Fase de captura: Los eventos se propagan desde el nodo más exterior hacia el nodo más interior. Esto se logra activando el tercer parámetro del addEventListener a true.
  2. Fase de burbuja: Después de que el evento se captura y llega al nodo objetivo, el evento burbujea de nuevo hacia los nodos exteriores. Por defecto, addEventListener usa la fase de burbuja.

Al hacer clic en el innerDiv, verás primero los mensajes de la fase de captura (desde el nodo más exterior hasta el más interior), y luego los de la fase de burbuja (del nodo más interior al exterior).

2.4.2. Ejemplo con stopPropagation

En este ejemplo, utilizaremos event.stopPropagation() para detener la propagación del evento en ambas fases.

<div id="outer" style="padding: 50px; background-color: lightblue;">
  Outer Div
  <div id="middle" style="padding: 50px; background-color: lightgreen;">
    Middle Div
    <div id="inner" style="padding: 50px; background-color: lightcoral;">
      Inner Div
    </div>
  </div>
</div>

<script>
  let innerDiv = document.getElementById('inner');
  let middleDiv = document.getElementById('middle');
  let outerDiv = document.getElementById('outer');

  innerDiv.addEventListener('click', function(event) {
    console.log('Clic en inner div');
    event.stopPropagation(); // Detiene la propagación
  });

  middleDiv.addEventListener('click', function() {
    console.log('Clic en middle div');
  });

  outerDiv.addEventListener('click', function() {
    console.log('Clic en outer div');
  });
</script>

Explicación:

  1. Al hacer clic en el innerDiv, solo se registrará el evento en el innerDiv, ya que event.stopPropagation() detiene la propagación hacia los nodos middleDiv y outerDiv.
  2. Si se hiciera clic en middleDiv, el evento se propagaría hasta outerDiv porque no se ha detenido la propagación.

2.4.3. Ejemplo avanzado: event.stopImmediatePropagation()

event.stopImmediatePropagation() detiene la propagación del evento y también evita que otros event listeners en el mismo nodo se ejecuten.

<div id="outer" style="padding: 50px; background-color: lightblue;">
  Outer Div
  <div id="middle" style="padding: 50px; background-color: lightgreen;">
    Middle Div
    <div id="inner" style="padding: 50px; background-color: lightcoral;">
      Inner Div
    </div>
  </div>
</div>

<script>
  let innerDiv = document.getElementById('inner');

  innerDiv.addEventListener('click', function() {
    console.log('Clic en inner div - primer listener');
  });

  innerDiv.addEventListener('click', function(event) {
    console.log('Clic en inner div - segundo listener');
    event.stopImmediatePropagation(); // Detiene otros listeners en el mismo nodo
  });

  innerDiv.addEventListener('click', function() {
    console.log('Clic en inner div - tercer listener');
  });
</script>

Explicación:

  1. Al hacer clic en el innerDiv, solo se ejecutarán el primer y segundo listener. El tercer listener no se ejecutará debido a stopImmediatePropagation().

3. Casos de Uso

3.1 Modificación de contenido en respuesta a eventos

Uno de los casos más comunes es modificar el contenido de la página en respuesta a las interacciones del usuario. Por ejemplo, un menú que se despliega al hacer clic en un botón.

<button id="toggleMenu">Abrir Menú</button>
<ul id="menu" style="display: none;">
  <li>Inicio</li>
  <li>Sobre Nosotros</li>
  <li>Contacto</li>
</ul>

<script>
  let menu = document.getElementById('menu');
  let toggleButton = document.getElementById('toggleMenu');

  toggleButton.addEventListener('click', function() {
    if (menu.style.display === 'none') {
      menu.style.display = 'block';
    } else {
      menu.style.display = 'none';
    }
  });
</script>

3.2 Formularios dinámicos

JavaScript permite validar formularios o añadir campos de forma dinámica según las acciones del usuario, lo que mejora la experiencia del usuario y reduce errores.

Ejemplo: Añadir y eliminar campos de formulario dinámicamente

Imagina un formulario en el que los usuarios pueden agregar varios correos electrónicos (o cualquier otro dato) de manera dinámica. En este caso, cada vez que el usuario hace clic en un botón "Agregar Email", se genera un nuevo campo de entrada de texto. También hay un botón para eliminar campos agregados si el usuario así lo desea.

<form id="emailForm">
  <div id="emailFields">
    <div class="emailField">
      <label for="email1">Email 1:</label>
      <input type="email" name="email[]" id="email1">
    </div>
  </div>
  <button type="button" id="addEmail">Agregar Email</button>
  <button type="submit">Enviar</button>
</form>
let emailCounter = 1; // Para contar cuántos campos de email hay
let emailForm = document.getElementById('emailForm');
let addEmailButton = document.getElementById('addEmail');
let emailFields = document.getElementById('emailFields');

// Función para agregar un nuevo campo de email
addEmailButton.addEventListener('click', function() {
  emailCounter++; // Aumenta el contador de emails

  // Crear un nuevo div contenedor para el email
  let newEmailDiv = document.createElement('div');
  newEmailDiv.classList.add('emailField');
  
  // Agregar un campo de entrada para el nuevo email
  newEmailDiv.innerHTML = `
    <label for="email${emailCounter}">Email ${emailCounter}:</label>
    <input type="email" name="email[]" id="email${emailCounter}">
    <button type="button" class="removeEmail">Eliminar</button>
  `;

  // Agregar el nuevo campo al contenedor de emails
  emailFields.appendChild(newEmailDiv);

  // Añadir funcionalidad para eliminar este campo de email
  newEmailDiv.querySelector('.removeEmail').addEventListener('click', function() {
    newEmailDiv.remove(); // Elimina el campo de email del formulario
  });
});

// Funcionalidad para enviar el formulario
emailForm.addEventListener('submit', function(event) {
  event.preventDefault(); // Evita el envío real del formulario
  
  let formData = new FormData(emailForm); // Recolecta todos los datos del formulario
  console.log('Emails ingresados:', formData.getAll('email[]')); // Muestra todos los emails en la consola

  // Aquí podrías procesar los datos o hacer un envío real usando fetch()
});

Explicación:

Ejercicio: Personalización de un Grid Dinámico

Objetivo: Crear un grid de imágenes interactivo, añadir botones que cambien tanto el layout del grid como el estilo de las imágenes al hacer clic en los botones.

  1. Cambios en el layout del grid:

  2. Cambios en el estilo de las imágenes:

  3. Interacción de las imágenes:

Puntos a evaluar: